﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Windows.Forms;
using Styx;
using Styx.Common;
using Styx.CommonBot;
using Styx.CommonBot.Routines;
using Styx.TreeSharp;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;
using Action = Styx.TreeSharp.Action;

namespace TuanHAPriest
{
    public partial class Classname : CombatRoutine
    {
        #region Delegates

        public delegate WoWPoint LocationRetriverDelegate(object context);

        #endregion

        #region Basic Functions

        public override bool WantButton
        {
            get { return true; }
        }

        public override WoWClass Class
        {
            get { return WoWClass.Priest; }
        }

        public override string Name
        {
            get { return "TuanHA Priest [Lazyraider Edition]"; }
        }

        public override Composite RestBehavior
        {
            get { return RestRotation(); }
        }

        public override Composite PreCombatBuffBehavior
        {
            get { return MainRotation(); }
        }

        //public override Composite PullBuffBehavior
        //{
        //    get { return MainRotation(); }
        //}

        //public override Composite PullBehavior
        //{
        //    get { return MainRotation(); }
        //}

        //public override Composite CombatBuffBehavior
        //{
        //    get { return MainRotation(); }
        //}

        public override Composite CombatBehavior
        {
            get { return MainRotation(); }
        }

        //public override Composite HealBehavior
        //{
        //    get { return MainRotation(); }
        //}

        //public override Composite MoveToTargetBehavior
        //{
        //    get { return CreateMoveToLosBehavior(); }
        //}

        public static LocalPlayer Me
        {
            get { return StyxWoW.Me; }
        }

        public override void Initialize()
        {
            Logging.Write("----------------------------------");
            Logging.Write("Hello " + Me.Race + " " + Me.Class);
            Logging.Write("Thank you for using TuanHA Priest");
            Logging.Write("----------------------------------");
            Logging.Write("Please be advised that this Combat Routine ONLY work with LazyRaider");
            Logging.Write("----------------------------------");
            Logging.Write("Use Control + " + IndexToKeys(THSettings.Instance.SPauseKey) + " to toggle Pause ON/OFF");
            Logging.Write("----------------------------------");
            Logging.Write("Use Control + " + IndexToKeys(THSettings.Instance.SAOEKey) + " to toggle AoE ON/OFF");
            Logging.Write("----------------------------------");
            Logging.Write("Use Control + " + IndexToKeys(THSettings.Instance.SHaloKey) +
                          " to toggle Halo/Cascade ON/OFF");
            Logging.Write("----------------------------------");
            Logging.Write("Use Control + " + IndexToKeys(THSettings.Instance.SModeKey) + " to toggle Normal/AoE Mode");
            Logging.Write("----------------------------------");

            //Disable for speed
            Lua.Events.AttachEvent("GROUP_ROSTER_UPDATE", UpdateGroupChangeEvent);
            Lua.Events.AttachEvent("ACTIVE_TALENT_GROUP_CHANGED", UpdateStatusEvent);
            //Lua.Events.AttachEvent("PLAYER_TALENT_UPDATE", UpdateMyTalentEvent);
            //Lua.Events.AttachEvent("GLYPH_ADDED", UpdateMyGlyphEvent);
            //Lua.Events.AttachEvent("WORLD_MAP_NAME_UPDATE", UpdateCurrentMapEvent);

            //BotEvents.Player.OnMapChanged += UpdateCurrentMapEvent;
            BotEvents.OnBotStarted += OnBotStartedEvent;

            //Set Camera angle so you never have problem with it
            //console cameraSmoothTrackingStyle 0 (credit by alxaw - member of CLU dev team) 
            //Lua.DoString("RunMacroText(\"/console cameraSmoothTrackingStyle 0\")");

            THSettings.Instance.UpdateStatus = true;

            THSettings.Instance.SPause = false;

            UpdateMyGlyph();
        }

        public override void OnButtonPress()
        {
            var gui = new THPriestGUI();
            gui.ShowDialog();
        }

        #endregion

        #region Function After this Region Order Alphabetically

        #endregion

        #region GetSpellCooldown

        public static TimeSpan GetSpellCooldown(string spell)
        {
            SpellFindResults results;
            if (SpellManager.FindSpell(spell, out results))
            {
                return results.Override != null ? results.Override.CooldownTimeLeft : results.Original.CooldownTimeLeft;
            }

            return TimeSpan.MaxValue;
        }

        #endregion

        #region GetUnits

        public List<WoWUnit> FarFriendlyPlayers = new List<WoWUnit>();
        public List<WoWUnit> FarFriendlyUnits = new List<WoWUnit>();
        public List<WoWPlayer> NearbyFriendlyPlayers = new List<WoWPlayer>();
        public List<WoWUnit> NearbyFriendlyUnits = new List<WoWUnit>();
        public List<WoWPlayer> NearbyUnFriendlyPlayers = new List<WoWPlayer>();
        public List<WoWUnit> NearbyUnFriendlyUnits = new List<WoWUnit>();

        //public List<WoWUnit> FarUnFriendlyPlayers = new List<WoWUnit>();//Don't use in this CC
        //public List<WoWUnit> FarUnFriendlyUnits = new List<WoWUnit>();

        public void GetUnits()
        {
            NearbyFriendlyPlayers.Clear();
            NearbyUnFriendlyPlayers.Clear();
            NearbyFriendlyUnits.Clear();
            NearbyUnFriendlyUnits.Clear();
            FarFriendlyPlayers.Clear();
            FarFriendlyUnits.Clear();

            NearbyFriendlyUnits.Add(Me);
            NearbyFriendlyPlayers.Add(Me);
            FarFriendlyUnits.Add(Me);
            FarFriendlyPlayers.Add(Me);

            IEnumerable<WoWUnit> units =
                ObjectManager.GetObjectsOfType<WoWUnit>(true, false).Where(p => ValidUnit(p) && p.Distance < 60);

            foreach (WoWUnit unit in units)
            {
                using (StyxWoW.Memory.AcquireFrame())
                {
                    if (IsMyPartyRaidMember(unit))
                    {
                        FarFriendlyUnits.Add(unit);

                        var player = unit as WoWPlayer;
                        if (player != null)
                        {
                            FarFriendlyPlayers.Add(unit);
                        }

                        if (unit.Distance < 40)
                        {
                            NearbyFriendlyUnits.Add(unit);

                            if (player != null)
                            {
                                NearbyFriendlyPlayers.Add(player);
                            }
                        }
                    }
                    else if (Me.CurrentMap.IsArena || Me.CurrentMap.IsBattleground)
                    {
                        if (unit.Distance > 40)
                        {
                            continue;
                        }
                        NearbyUnFriendlyUnits.Add(unit);

                        var player = unit as WoWPlayer;
                        if (player != null)
                        {
                            NearbyUnFriendlyPlayers.Add(player);
                        }
                    }
                    else if (IsEnemy(unit))
                    {
                        if (unit.Distance > 40)
                        {
                            continue;
                        }
                        NearbyUnFriendlyUnits.Add(unit);

                        var player = unit as WoWPlayer;
                        if (player != null)
                        {
                            NearbyUnFriendlyPlayers.Add(player);
                        }
                    }
                }
            }
        }

        #endregion

        #region GetUnitHeal

        public WoWUnit UnitHeal;

        private void GetUnitHeal()
        {
            UnitHeal = null;
            //Tsulong override
            if (Me.GotTarget && Me.CurrentTarget.Entry == 62442 &&
                (!Me.CurrentTarget.Attackable || Me.CurrentTarget.IsFriendly))
            {
                if (UnitHeal == null || (UnitHeal != null && UnitHeal.HealthPercent <= THSettings.Instance.SUrgentHeal))
                {
                    UnitHeal = Me.CurrentTarget;
                    return;
                }
            }
            if (Healable(Me.CurrentTarget) && Me.CurrentTarget != UnitHealFull &&
                Me.CurrentTarget.HealthPercent < THSettings.Instance.SDoNotHealAbove &&
                (Me.CurrentTarget.IsPlayer || Me.CurrentTarget.IsPet))
            {
                UnitHeal = Me.CurrentTarget;
            }
            else if (Healable(Me.FocusedUnit) && Me.FocusedUnit != UnitHealFull &&
                     Me.FocusedUnit.HealthPercent < THSettings.Instance.SDoNotHealAbove &&
                     (Me.FocusedUnit.IsPlayer || Me.FocusedUnit.IsPet) && !Me.CurrentMap.IsArena)
            {
                UnitHeal = Me.FocusedUnit;
            }
            else if (Me.HealthPercent < THSettings.Instance.SPriorityHeal &&
                     Me != UnitHealFull && !Me.CurrentMap.IsArena)
            {
                UnitHeal = Me;
            }
            else
            {
                using (StyxWoW.Memory.AcquireFrame())
                {
                    UnitHeal = (from unit in NearbyFriendlyUnits
                                where !Blacklist.Contains(unit.Guid)
                                orderby unit.HealthPercent ascending
                                where unit != UnitHealFull
                                where unit.IsPlayer || Me.CurrentMap.IsArena && unit.IsPet
                                where HealableValid(unit)
                                select unit).FirstOrDefault();
                }
            }
        }

        #endregion

        #region GetUnitAttack

        public WoWUnit UnitAttack;
        public WoWUnit UnitAttackPet;

        private void GetUnitAttack()
        {
            UnitAttack = null;
            UnitAttackPet = null;

            //if (THSettings.Instance.SAutoTarget)
            //{
            using (StyxWoW.Memory.AcquireFrame())
            {
                if (!SpellManager.HasSpell("Holy Insight"))
                {
                    UnitAttack = (from unit in NearbyUnFriendlyUnits
                                  //Only search Target if in instance
                                  //where Me.IsInInstance
                                  orderby unit.Distance ascending
                                  //attack any unit if not in dungeon/raid
                                  where unit.Combat
                                  //where unit.GotTarget
                                  //only attack unit not tagged
                                  where
                                      unit.IsTargetingMeOrPet ||
                                      unit.IsTargetingMyPartyMember ||
                                      unit.IsTargetingMyRaidMember ||
                                      unit.IsPlayer && (Me.CurrentMap.IsBattleground || Me.CurrentMap.IsArena)
                                  where AttackableValid(unit)
                                  select unit).FirstOrDefault();
                }
                else
                {
                    UnitAttack = (from unit in NearbyUnFriendlyUnits
                                  //Only search Target if in instance
                                  //where Me.IsInInstance
                                  orderby unit.HealthPercent ascending
                                  //attack any unit if not in dungeon/raid
                                  where unit.Combat
                                  //where unit.GotTarget
                                  //only attack unit not tagged
                                  where
                                      unit.IsTargetingMeOrPet ||
                                      unit.IsTargetingMyPartyMember ||
                                      unit.IsTargetingMyRaidMember ||
                                      unit.IsPlayer && (Me.CurrentMap.IsBattleground || Me.CurrentMap.IsArena)
                                  where AttackableValid(unit)
                                  select unit).FirstOrDefault();
                }
                //if it pet, try to find a player
                if (UnitAttack != null && !UnitAttack.IsPlayer && !Me.CurrentMap.IsRaid && !Me.CurrentMap.IsDungeon)
                {
                    UnitAttackPet = UnitAttack;
                    UnitAttack = (from unit in NearbyUnFriendlyPlayers
                                  orderby unit.Distance ascending
                                  where unit.Combat && unit.IsTargetingMeOrPet || Me.CurrentMap.IsBattleground
                                  where AttackableValid(unit)
                                  select unit).FirstOrDefault();

                    //can't find a player, kill the pet
                    if (UnitAttack == null && UnitAttackPet != null)
                    {
                        UnitAttack = UnitAttackPet;
                    }
                }
            }
            //}
        }

        #endregion

        #region GroupMembers

        internal static IEnumerable<WoWPartyMember> GroupMembers
        {
            get { return !Me.GroupInfo.IsInRaid ? Me.GroupInfo.PartyMembers : Me.GroupInfo.RaidMembers; }
        }

        #endregion

        #region Hold

        public static string MyCastingSpells = 
            "[Renew] [Heal] [Flash Heal] [Divine Plea] [Holy Radiance] [Holy Shock] [Light of Dawn] [Word of Glory] [Denounce] [Repentance]";

        public Composite Hold()
        {
            return
                new Decorator(
                    ret =>
                    Me.HasAura("Drink") || Me.HasAura("Food") ||
                    Me.IsCasting &&
                    (Me.ChanneledCastingSpellId != 0 ||
                     !MyCastingSpells.Contains(Me.CastingSpell.Name)) ||
                    !Me.IsAlive,
                    new Action(delegate { return RunStatus.Success; })
                    );
        }

        #endregion

        #region Pulse

        public bool SPause = false;
        public bool UseHalo = true;
        public DateTime LastSwitch;
        public bool UseAoE = true;
        public bool SModeNormal = true;

        public override void Pulse()
        {
            //ObjectManager.Update();
            GetUnits();
            GetUnitAttack();

            //Pause
            if (GetAsyncKeyState(Keys.LControlKey) < 0 &&
                GetAsyncKeyState(IndexToKeys(THSettings.Instance.SPauseKey)) < 0 &&
                LastSwitch.AddSeconds(1) < DateTime.Now) // && GetActiveWindowTitle() == "World of Warcraft")
            {
                if (SPause)
                {
                    SPause = false;
                    LastSwitch = DateTime.Now;
                    Logging.Write("Pause Mode is OFF, Ctrl + " + IndexToKeys(THSettings.Instance.SPauseKey) +
                                  " to Override bot action.");
                    Lua.DoString("RunMacroText(\"/script msg='Pause Mode OFF' print(msg)\")");
                }
                else
                {
                    SPause = true;
                    LastSwitch = DateTime.Now;
                    Logging.Write("Pause Mode is ON, Ctrl + " + IndexToKeys(THSettings.Instance.SPauseKey) +
                                  " to resume bot action.");
                    Lua.DoString("RunMacroText(\"/script msg='Pause Mode ON' print(msg)\")");
                }
            }

            if (GetAsyncKeyState(Keys.LControlKey) < 0 &&
                GetAsyncKeyState(IndexToKeys(THSettings.Instance.SHaloKey)) < 0 &&
                LastSwitch.AddSeconds(1) < DateTime.Now)
            {
                if (UseHalo)
                {
                    UseHalo = false;
                    LastSwitch = DateTime.Now;
                    //Lua.DoString("RunMacroText(\"/s Halo is OFF\")");
                    Lua.DoString("RunMacroText(\"/script msg='Halo / Cascade OFF' print(msg)\")");
                }
                else
                {
                    UseHalo = true;
                    LastSwitch = DateTime.Now;
                    Lua.DoString("RunMacroText(\"/script msg='Halo / Cascade ON' print(msg)\")");
                }
            }

            if (GetAsyncKeyState(Keys.LControlKey) < 0 && GetAsyncKeyState(IndexToKeys(THSettings.Instance.SAOEKey)) < 0 &&
                LastSwitch.AddSeconds(1) < DateTime.Now)
            {
                if (UseAoE)
                {
                    UseAoE = false;
                    LastSwitch = DateTime.Now;
                    //Lua.DoString("RunMacroText(\"/s Halo is OFF\")");
                    Lua.DoString("RunMacroText(\"/script msg='AoE OFF' print(msg)\")");
                }
                else
                {
                    UseAoE = true;
                    LastSwitch = DateTime.Now;
                    Lua.DoString("RunMacroText(\"/script msg='AoE ON' print(msg)\")");
                }
            }

            //Mode
            if (GetAsyncKeyState(Keys.LControlKey) < 0 &&
                GetAsyncKeyState(IndexToKeys(THSettings.Instance.SModeKey)) < 0 &&
                LastSwitch.AddSeconds(1) < DateTime.Now) // && GetActiveWindowTitle() == "World of Warcraft")
            {
                if (SModeNormal)
                {
                    SModeNormal = false;
                    LastSwitch = DateTime.Now;
                    Logging.Write("Using Muti-Dot Mode!");
                    Logging.Write(
                        "This mode will make Multi-dotting and using Procs the highest priority, Best for boss fights with multipul targets.");
                    Logging.Write("Ctrl + " + IndexToKeys(THSettings.Instance.SModeKey) + " to switch to Normal Mode.");
                    Lua.DoString("RunMacroText(\"/script msg='Using Muti-Dot Mode!' print(msg)\")");
                    Lua.DoString(
                        "RunMacroText(\"/script msg='This mode will make Multi-dotting and using Procs the highest priority, Best for boss fights with multipul targets.' print(msg)\")");
                }
                else
                {
                    SModeNormal = true;
                    LastSwitch = DateTime.Now;
                    Logging.Write("Using Normal Mode!");
                    Logging.Write("This mode will use the best Single Target Rotation with some Multidot/AOE support.");
                    Logging.Write("Ctrl + " + IndexToKeys(THSettings.Instance.SModeKey) + " to switch to Muti-Dot Mode.");
                    Lua.DoString("RunMacroText(\"/script msg='Using Normal Mode!' print(msg)\")");
                    Lua.DoString(
                        "RunMacroText(\"/script msg='This mode will use the best Single Target Rotation with some Multidot/AOE support.' print(msg)\")");
                }
            }
        }

        #endregion

        #region MainRotation

        public static bool HelloFriend = false;
        public static bool IsConstantFace;
        public static bool IsOverrideModeOn = false;
        public static WoWUnit MyLastTarget;
        public static DateTime Now = DateTime.Now;
        public static Stopwatch PendingSpellStopWatch = new Stopwatch();
        public static DateTime TickMilisecond;
        public static DateTime TickMilisecondTotal;
        public static WoWUnit UnitHealFull;
        public static DateTime LastVT;
        public WoWUnit LastVTTarget;

        public static bool CurrentTargetIsValid;
        public static bool CurrentTargetIsEnemy;
        public static bool CurrentTargetIsInrange;

        private static readonly HashSet<uint> SafeTarget = new HashSet<uint>
                                                               {
                                                                   60410, //Elegon
                                                                   60776, //Empyreal Focus
                                                                   60793, //Celestial Protector
                                                                   60913, //Energy Charge
                                                               };

        public Composite MainRotation()
        {
            return new PrioritySelector(
                new Decorator(
                    ret => SPause,
                    new Action(delegate { return RunStatus.Success; })),
                new Decorator(ret => (!StyxWoW.IsInGame || !StyxWoW.IsInWorld || !Me.IsValid),
                              new Action(delegate
                                             {
                                                 Logging.Write(LogLevel.Diagnostic, "Error, Skip Main Rotation.");
                                                 return RunStatus.Success;
                                             })),
                new Action(delegate
                               {
                                   //Hold All Action On Mass Dispel
                                   if (Me.HasPendingSpell("Mass Dispel"))
                                   {
                                       Logging.Write(LogLevel.Diagnostic,
                                                     DateTime.Now.ToString("ss:fff ") +
                                                     "Hold All Action On Mass Dispel");
                                       return RunStatus.Success;
                                   }

                                   //Hold All Action On Tranqulity
                                   if (Me.IsChanneling && Me.ChanneledCastingSpellId == 740)
                                   {
                                       Logging.Write(LogLevel.Diagnostic,
                                                     DateTime.Now.ToString("ss:fff ") +
                                                     "Hold All Action On Tranqulity");
                                       return RunStatus.Success;
                                   }

                                   //Hold rotation on Hymn of Hope
                                   if (Me.IsChanneling && Me.ChanneledCastingSpellId == 64901)
                                   {
                                       Logging.Write(LogLevel.Diagnostic,
                                                     DateTime.Now.ToString("ss:fff ") +
                                                     "Hold All Action On Hymn of Hope");
                                       return RunStatus.Success;
                                   }

                                   //Clear Target if dead and still in combat
                                   if (Me.CurrentTarget != null && !Me.CurrentTarget.IsAlive && Me.Combat)
                                   {
                                       Lua.DoString("RunMacroText(\"/cleartarget\")");
                                   }

                                   //Clear Target if walk away from friendly
                                   if (Me.CurrentTarget != null && !Me.CurrentTarget.IsPlayer &&
                                       Me.CurrentTarget.IsFriendly &&
                                       Me.CurrentTarget.IsSafelyBehind(Me))
                                   {
                                       Lua.DoString("RunMacroText(\"/cleartarget\")");
                                   }

                                   //Hold dps on Dungeon or Raid
                                   if ((Me.CurrentMap.IsDungeon || Me.CurrentMap.IsRaid) &&
                                       Me.CurrentTarget != null &&
                                       !Me.CurrentTarget.Combat)
                                   {
                                       return RunStatus.Success;
                                   }

                                   //"Mind Spike" - 73510
                                   if (Me.IsCasting && Me.CastingSpellId == 73510 &&
                                       !Me.HasAura("Surge of Darkness"))
                                   {
                                       SpellManager.StopCasting();
                                   }

                                   //Fix bug casting Smite
                                   if (Me.IsCasting && Me.CastingSpellId == 585)
                                   {
                                       SpellManager.StopCasting();
                                       CastSpell("Shadowform", Me);
                                   }

                                   //if (Me.IsChanneling)
                                   //{
                                   //    Logging.Write(Me.ChanneledSpell.Name + " " + Me.CurrentChannelTimeLeft.TotalMilliseconds);
                                   //}

                                   //if (Me.IsCasting)
                                   //{
                                   //    Logging.Write(Me.CastingSpell.Name + " " + Me.CurrentCastTimeLeft.TotalMilliseconds);
                                   //}

                                   CurrentTargetIsValid = ValidUnit(Me.CurrentTarget) ||
                                                          Me.CurrentTarget != null &&
                                                          SafeTarget.Contains(Me.CurrentTarget.Entry);
                                   CurrentTargetIsEnemy = IsEnemy(Me.CurrentTarget);

                                   return RunStatus.Failure;
                               }),
                UseHealthstone(),
                UseBattleStandard(),
                Fade(),
                DesperatePrayer(),
                new Decorator(ret =>
                              SModeNormal, //NORMAL MODE
                              new PrioritySelector(
							      DesperatePrayer(),
                                  VampiricEmbrace(),
								  MindSear5(),
                                  DevouringPlague(),
                                  Cascade(),
                                  Halo(),
								  MindSpike(),
                                  MindBlastInstant(),
                                  SWD(),
                                  MindBlast(),
                                  ShadowWordPain(),
                                  VampiricTouch(),
                                  SWDOT(),
                                  ShadowWordPainMulti(),
                                  VampiricTouchMulti(),
                                  Shadowfiend(),
                                  ShadowWordPainMoving(),
                                  MindFlay(),
                                  Dispersion(),
                                  PowerWordShield(),
                                  Shadowform(),
                                  InnerFire(),
                                  PowerWordFortitude()
                                  )),
                new Decorator(ret =>
                              !SModeNormal, //MULTI DOT MODE
                              new PrioritySelector(
							      DesperatePrayer(),
								  MindSear5(),
								  VampiricEmbrace(),
                                  DevouringPlague(),
                                  ShadowWordPainMulti(),
                                  VampiricTouchMulti(),
                                  MindBlastInstant(),
                                  MindSpike(),
                                  Cascade(),
                                  Halo(),
                                  MindBlastInstant(),
                                  SWD(),
                                  MindBlast(),
                                  ShadowWordPain(),
                                  VampiricTouch(),
                                  SWDOT(),
                                  Shadowfiend(),
                                  ShadowWordPainMoving(),
                                  MindFlay(),
                                  Dispersion(),
                                  PowerWordShield(),
                                  Shadowform(),
                                  InnerFire(),
                                  PowerWordFortitude()
                                  ))
                );
        }

        #endregion
    }
}